package com.tiger.lbs;

import com.tiger.utils.MathUtils;

/**
 * 经纬度
 * @author andy
 *
 */
public class Coordinate {
	/**
	 * 经度
	 */
	private final double lng;

	/**
	 * 纬度
	 */
	private final double lat;
	
	public Coordinate() {
		this(0, 0);
	}
	

	public Coordinate(double lng, double lat) {
		this.lng = lng;
		this.lat = lat;
	}


	public double getLng() {
		return lng;
	}

	public double getLat() {
		return lat;
	}
	
	@Override
	public String toString() {
		return "{lng:" + lng + ", lat:" + lat + "}";
	}


	/**
	 * 是否在target的radius范围内
	 * @param target
	 * @param radius
	 * @return
	 */
	public boolean isInRangeOf(Coordinate target, int radius) {
		return distance(target) <= radius;
	}
	
	/**
	 * 计算跟坐标点target的距离(米)
	 * @param target
	 * @return
	 */
	public double distance(Coordinate target) {
		if(target == null) {
			return 0;
		}
		Coordinate a = new Coordinate(ew(this.getLng(), -180, 180), lw(this.getLat(), -74, 74));
		Coordinate b = new Coordinate(ew(target.getLng(), -180, 180), lw(target.getLat(), -74, 74));
		return Td(oi(a.getLng()), oi(b.getLng()), oi(a.getLat()), oi(b.getLat()));
	}
	
	/**
	 * 计算与两个点的距离(米)
	 * @param destA
	 * @param destB
	 * @return
	 */
	public double distance(Coordinate destA, Coordinate destB) {
		double ab = destA.distance(destB);
		double ac = destA.distance(this);
		double bc = destB.distance(this);
		double angleB = MathUtils.angleA(ac, bc, ab);
		if(angleB >= 90) {
			return bc;
		}
		double angleA = MathUtils.angleA(bc, ac, ab);
		if(angleA >= 90) {
			return ac;
		}
		return bc * Math.sin(angleB * Math.PI / 180);
	}
	
	
	
	private static double A = 6378245.0;
	private static double EE = 0.00669342162296594323;
	private final static double X_PI = Math.PI * 3000.0 / 180.0;

	public Coordinate wgs2bd() {
		return this.wgs2gcj().gcj2bd();
	}

	private Coordinate wgs2gcj() {
		double lat = this.getLat();
		double lon = this.getLng();
		double dLat = transformLat(lon - 105.0, lat - 35.0);
		double dLon = transformLon(lon - 105.0, lat - 35.0);
		double radLat = lat / 180.0 * Math.PI;
		double magic = Math.sin(radLat);
		magic = 1 - EE * magic * magic;
		double sqrtMagic = Math.sqrt(magic);
		dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * Math.PI);
		dLon = (dLon * 180.0) / (A / sqrtMagic * Math.cos(radLat) * Math.PI);
		double mgLat = lat + dLat;
		double mgLon = lon + dLon;
		return new Coordinate(mgLon, mgLat);
	}
	
	private Coordinate gcj2bd() {
		double lat = this.getLat();
		double lon = this.getLng();
		double x = lon, y = lat;
		double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * X_PI);
		double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * X_PI);
		double bd_lon = z * Math.cos(theta) + 0.0065;
		double bd_lat = z * Math.sin(theta) + 0.006;
		return new Coordinate(bd_lon, bd_lat);
	}

	private static double transformLat(double lat, double lon) {
		double ret = -100.0 + 2.0 * lat + 3.0 * lon + 0.2 * lon * lon + 0.1 * lat * lon + 0.2 * Math.sqrt(Math.abs(lat));
		ret += (20.0 * Math.sin(6.0 * lat * Math.PI) + 20.0 * Math.sin(2.0 * lat * Math.PI)) * 2.0 / 3.0;
		ret += (20.0 * Math.sin(lon * Math.PI) + 40.0 * Math.sin(lon / 3.0 * Math.PI)) * 2.0 / 3.0;
		ret += (160.0 * Math.sin(lon / 12.0 * Math.PI) + 320 * Math.sin(lon * Math.PI  / 30.0)) * 2.0 / 3.0;
		return ret;
	}

	private static double transformLon(double lat, double lon) {
		double ret = 300.0 + lat + 2.0 * lon + 0.1 * lat * lat + 0.1 * lat * lon + 0.1 * Math.sqrt(Math.abs(lat));
		ret += (20.0 * Math.sin(6.0 * lat * Math.PI) + 20.0 * Math.sin(2.0 * lat * Math.PI)) * 2.0 / 3.0;
		ret += (20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin(lat / 3.0 * Math.PI)) * 2.0 / 3.0;
		ret += (150.0 * Math.sin(lat / 12.0 * Math.PI) + 300.0 * Math.sin(lat / 30.0 * Math.PI)) * 2.0 / 3.0;
		return ret;
	}
	
	private static double lw(double a, double b, double c) {
	    a = Math.max(a,b);
	    a = Math.min(a,c);
	    return a;
	}
	
	private static double ew(double a, double b, double c) {
	    while(a > c) {
	        a -= c - b;
	    }
	    while(a < b) {
	    	a += c - b;
	    }
	    return a;
	}

	private static double oi(double a) {
		return Math.PI * a / 180;
	}
	
	private static double Td(double a, double b, double c, double d) {
		return 6370996.81 * Math.acos(Math.sin(c) * Math.sin(d) + Math.cos(c) * Math.cos(d) * Math.cos(b - a));
	}
	
	public static boolean isInChina(double lng, double lat) {
		return lng >= 73.49444 && lng <= 135.143782 && lat >= 3.29813 && lat <= 53.571251;
	}
}
